#include <stdio.h>
#include <string.h>
#include <stdarg.h>

/* common */
#include "check.h"
#include "cpu.h"
#include "inttypes.h"
#include "strbuf.h"
#include "xmalloc.h"
#include "xsprintf.h"
#include "version.h"

/* cc65 */
#include "asmcode.h"
#include "asmlabel.h"
#include "casenode.h"
#include "codeseg.h"
#include "dataseg.h"
#include "error.h"
#include "global.h"
#include "segments.h"
#include "stackptr.h"
#include "stdfunc.h"
#include "textseg.h"
#include "util.h"
#include "codegen.h"

static void ucmp(unsigned flags, unsigned long val) {
  if (flags & CF_CONST) {
    switch (flags & CF_TYPEMASK) {
    case CF_CHAR:
      if (flags & CF_FORCECHAR) {
        AddCodeLine("sec");
        AddCodeLine("lda A");
        AddCodeLine("sub #%d", (unsigned char)val);
        AddCodeLine("sta TMP0");
        AddCodeLine("lda #0");
        AddCodeLine("sl");
        AddCodeLine("sta TMP1");
        break;
      }
      // FALLTHROUGH
    case CF_INT:
      AddCodeLine("sec");
      AddCodeLine("lda A");
      AddCodeLine("sub #%d", (unsigned char)val);
      AddCodeLine("sta TMP0");
      AddCodeLine("lda A,#1");
      AddCodeLine("sub #%d", (unsigned char)(val >> 8));
      AddCodeLine("or TMP0");
      AddCodeLine("sta TMP0");
      AddCodeLine("lda #0");
      AddCodeLine("sl");
      AddCodeLine("sta TMP1");
      break;
    case CF_LONG:
      AddCodeLine("sec");
      AddCodeLine("lda A");
      AddCodeLine("sub #%d", (unsigned char)val);
      AddCodeLine("sta TMP0");
      AddCodeLine("lda A,#1");
      AddCodeLine("sub #%d", (unsigned char)(val >> 8));
      AddCodeLine("or TMP0");
      AddCodeLine("sta TMP0");
      AddCodeLine("lda A,#2");
      AddCodeLine("sub #%d", (unsigned char)(val >> 16));
      AddCodeLine("or TMP0");
      AddCodeLine("sta TMP0");
      AddCodeLine("lda A,#3");
      AddCodeLine("sub #%d", (unsigned char)(val >> 24));
      AddCodeLine("or TMP0");
      AddCodeLine("sta TMP0");
      AddCodeLine("lda #0");
      AddCodeLine("sl");
      AddCodeLine("sta TMP1");
      break;
    default:
      typeerror(flags);
      break;
    }
  } else {
    switch (flags & CF_TYPEMASK) {
    case CF_CHAR:
      if (flags & CF_FORCECHAR) {
        AddCodeLine("sec");
        AddCodeLine("lda (SP)");
        AddCodeLine("sta TMP0");
        AddCodeLine("lda #0");
        AddCodeLine("sl");
        AddCodeLine("sta TMP1");
        break;
      }
      // FALLTHROUGH
    case CF_INT:
      AddCodeLine("sec");
      AddCodeLine("lda (SP)");
      AddCodeLine("sub A");
      AddCodeLine("sta TMP0");
      AddCodeLine("lda (SP),#1");
      AddCodeLine("sub A,#1");
      AddCodeLine("or TMP0");
      AddCodeLine("sta TMP0");
      AddCodeLine("lda #0");
      AddCodeLine("sl");
      AddCodeLine("sta TMP1");
      break;
    case CF_LONG:
      AddCodeLine("sec");
      AddCodeLine("lda (SP)");
      AddCodeLine("sub A");
      AddCodeLine("sta TMP0");
      AddCodeLine("lda (SP),#1");
      AddCodeLine("sub A,#1");
      AddCodeLine("or TMP0");
      AddCodeLine("sta TMP0");
      AddCodeLine("lda (SP),#2");
      AddCodeLine("sub A,#2");
      AddCodeLine("or TMP0");
      AddCodeLine("sta TMP0");
      AddCodeLine("lda (SP),#3");
      AddCodeLine("sub A,#3");
      AddCodeLine("or TMP0");
      AddCodeLine("sta TMP0");
      AddCodeLine("lda #0");
      AddCodeLine("sl");
      AddCodeLine("sta TMP1");
      break;
    default:
      typeerror(flags);
      break;
    }

    g_drop(sizeofarg(flags));
    pop(flags);
  }
}

static void icmp(unsigned flags, unsigned long val) {
  if (flags & CF_CONST) {
    switch (flags & CF_TYPEMASK) {
    case CF_CHAR:
      if (flags & CF_FORCECHAR) {
        AddCodeLine("clc");
        AddCodeLine("lda A");
        AddCodeLine("add #0x80");
        AddCodeLine("sta A");

        val = val + 0x80;
        break;
      }
      // FALLTHROUGH
    case CF_INT:
      AddCodeLine("clc");
      AddCodeLine("lda A,#1");
      AddCodeLine("add #0x80");
      AddCodeLine("sta A,#1");

      val = val + 0x8000;
      break;
    case CF_LONG:
      AddCodeLine("clc");
      AddCodeLine("lda A,#3");
      AddCodeLine("add #0x80");
      AddCodeLine("sta A,#3");
      val = val + 0x80000000;
      break;
    default:
      typeerror(flags);
      break;
    }
  } else {
    switch (flags & CF_TYPEMASK) {
    case CF_CHAR:
      if (flags & CF_FORCECHAR) {
        AddCodeLine("clc");
        AddCodeLine("lda (SP)");
        AddCodeLine("add #0x80");
        AddCodeLine("sta (SP)");

        AddCodeLine("clc");
        AddCodeLine("lda A");
        AddCodeLine("add #0x80");
        AddCodeLine("sta A");

        break;
      }
      // FALLTHROUGH
    case CF_INT:
      AddCodeLine("clc");
      AddCodeLine("lda (SP),#1");
      AddCodeLine("add #0x80");
      AddCodeLine("sta (SP),#1");

      AddCodeLine("clc");
      AddCodeLine("lda A,#1");
      AddCodeLine("add #0x80");
      AddCodeLine("sta A,#1");
      break;
    case CF_LONG:
      AddCodeLine("clc");
      AddCodeLine("lda (SP),#3");
      AddCodeLine("add #0x80");
      AddCodeLine("sta (SP),#3");

      AddCodeLine("clc");
      AddCodeLine("lda A,#3");
      AddCodeLine("add #0x80");
      AddCodeLine("sta A,#3");
      break;
    default:
      typeerror(flags);
      break;
    }
  }

  ucmp(flags | CF_UNSIGNED, val);
}

// TMP0 = byte_or(TOS-A)
// TMP1的值，只能是1或是0：1-无借位，0-有借位
// If the right hand side is const, the lhs is not on stack but still in the primary register.
static void cmp(unsigned flags, unsigned long val) {
  if (flags & CF_UNSIGNED) {
    ucmp(flags, val);
  } else {
    icmp(flags, val);
  }
}

void g_scale(unsigned flags, long val)
/* Scale the value in the primary register by the given value. If val is positive,
** scale up, is val is negative, scale down. This function is used to scale
** the operands or results of pointer arithmetic by the size of the type, the
** pointer points to.
*/
{
  AddCodeLine_("debug %s", __FUNCTION__);
  int p2;

  /* Value may not be zero */
  if (val == 0) {
    Internal("Data type has no size");
  } else if (val > 0) {
    /* Scale up */
    if ((p2 = PowerOf2(val)) > 0 && p2 <= 4) {
      /* Factor is 2, 4, 8 and 16, use special function */
      g_asl(flags | CF_CONST, p2);
    } else if (val != 1) {
      /* Use a multiplication instead */
      g_mul(flags | CF_CONST, val);
    }
  } else {
    /* Scale down */
    val = -val;
    if ((p2 = PowerOf2(val)) > 0 && p2 <= 4) {
      /* Factor is 2, 4, 8 and 16 use special function */
      g_asr(flags | CF_CONST, p2);
    } else if (val != 1) {

      /* Use a division instead */
      g_div(flags | CF_CONST, val);
    }
  }
}

/*****************************************************************************/
/*              Adds and subs of variables fix a fixed address               */
/*****************************************************************************/

void g_addlocal(unsigned flags, int offs)
/* Add a local variable to ax */
{
  AddCodeLine_("debug %s", __FUNCTION__);
  /* Correct the offset and check it */
  offs -= StackPtr;
  CheckLocalOffs(offs);

  AddCodeLine("clc");
  switch (flags & CF_TYPEMASK) {
  case CF_CHAR:
    AddCodeLine("lda A");
    AddCodeLine("add (SP),#%d", offs);
    AddCodeLine("sta A");
    AddCodeLine("lda A,#1");
    AddCodeLine("add #0");
    AddCodeLine("sta A,#1");
    break;
  case CF_INT:
    AddCodeLine("lda A");
    AddCodeLine("add (SP),#%d", offs);
    AddCodeLine("sta A");
    AddCodeLine("lda A,#1");
    AddCodeLine("add (SP),#%d", offs + 1);
    AddCodeLine("sta A,#1");
    break;
  case CF_LONG:
    AddCodeLine("lda A");
    AddCodeLine("add (SP),#%d", offs);
    AddCodeLine("sta A");
    AddCodeLine("lda A,#1");
    AddCodeLine("add (SP),#%d", offs + 1);
    AddCodeLine("sta A,#1");
    AddCodeLine("lda A,#2");
    AddCodeLine("add (SP),#%d", offs + 2);
    AddCodeLine("sta A,#2");
    AddCodeLine("lda A,#3");
    AddCodeLine("add (SP),#%d", offs + 3);
    AddCodeLine("sta A,#3");

    break;
  default:
    typeerror(flags);
  }
}

void g_addstatic(unsigned flags, uintptr_t label, long offs)
/* Add a static variable to ax */
{
  AddCodeLine_("debug %s", __FUNCTION__);
  /* Create the correct label name */
  const char *lbuf = GetLabelName(flags, label, offs);

  AddCodeLine("clc");
  switch (flags & CF_TYPEMASK) {

  case CF_CHAR:
    AddCodeLine("lda A");
    AddCodeLine("add %s", lbuf);
    AddCodeLine("sta A");
    AddCodeLine("lda A,#1");
    AddCodeLine("add #0");
    AddCodeLine("sta A,#1");
    break;

  case CF_INT:
    AddCodeLine("lda A");
    AddCodeLine("add %s", lbuf);
    AddCodeLine("sta A");
    AddCodeLine("lda A,#1");
    AddCodeLine("add %s,#1", lbuf);
    AddCodeLine("sta A,#1");
    break;

  case CF_LONG:
    AddCodeLine("lda A");
    AddCodeLine("add %s", lbuf);
    AddCodeLine("sta A");
    AddCodeLine("lda A,#1");
    AddCodeLine("add %s,#1", lbuf);
    AddCodeLine("sta A,#1");
    AddCodeLine("lda A,#2");
    AddCodeLine("add %s,#2", lbuf);
    AddCodeLine("sta A,#2");
    AddCodeLine("lda A,#3");
    AddCodeLine("add %s,#3", lbuf);
    AddCodeLine("sta A,#3");
    break;

  default:
    typeerror(flags);
  }
}

/*****************************************************************************/
/*                           Special op= functions                           */
/*****************************************************************************/

void g_addeqstatic(unsigned flags, uintptr_t label, long offs, unsigned long val)
/* Emit += for a static variable */
{
  AddCodeLine_("debug %s", __FUNCTION__);
  /* Create the correct label name */
  const char *lbuf = GetLabelName(flags, label, offs);

  AddCodeLine("clc");
  if (flags & CF_CONST) {
    switch (flags & CF_TYPEMASK) {
    case CF_CHAR:
      if (flags & CF_FORCECHAR) {
        AddCodeLine("lda %s", lbuf);
        AddCodeLine("add #%d", (unsigned char)val);
        AddCodeLine("sta %s", lbuf);
        AddCodeLine("sta A");
        if (flags & CF_UNSIGNED) {
          AddCodeLine("lda #0");
          AddCodeLine("sta A,#1");
        } else {
          unsigned L = GetLocalLabel();
          AddCodeLine("and #0x80");
          AddCodeLine("jz %s", LocalLabelName(L));
          AddCodeLine("lda #0xff");
          g_defcodelabel(L);
          AddCodeLine("sta A,#1");
        }
        break;
      }
    case CF_INT:
      AddCodeLine("lda %s", lbuf);
      AddCodeLine("add #%d", (unsigned char)val);
      AddCodeLine("sta %s", lbuf);
      AddCodeLine("sta A");
      AddCodeLine("lda %s,#1", lbuf);
      AddCodeLine("add #%d", (unsigned char)(val >> 8));
      AddCodeLine("sta %s,#1", lbuf);
      AddCodeLine("sta A,#1");
      break;
    case CF_LONG:
      AddCodeLine("lda %s", lbuf);
      AddCodeLine("add #%d", (unsigned char)val);
      AddCodeLine("sta %s", lbuf);
      AddCodeLine("sta A");
      AddCodeLine("lda %s,#1", lbuf);
      AddCodeLine("add #%d", (unsigned char)(val >> 8));
      AddCodeLine("sta %s,#1", lbuf);
      AddCodeLine("sta A,#1");
      AddCodeLine("lda %s,#2", lbuf);
      AddCodeLine("add #%d", (unsigned char)(val >> 16));
      AddCodeLine("sta %s,#2", lbuf);
      AddCodeLine("sta A,#2");
      AddCodeLine("lda %s,#3", lbuf);
      AddCodeLine("add #%d", (unsigned char)(val >> 24));
      AddCodeLine("sta %s,#3", lbuf);
      AddCodeLine("sta A,#3");
      break;
    default:
      typeerror(flags);
    }
  } else {
    switch (flags & CF_TYPEMASK) {
    case CF_CHAR:
      if (flags & CF_FORCECHAR) {
        AddCodeLine("lda %s", lbuf);
        AddCodeLine("add A");
        AddCodeLine("sta %s", lbuf);
        AddCodeLine("sta A");
        if (flags & CF_UNSIGNED) {
          AddCodeLine("lda #0");
          AddCodeLine("sta A,#1");
        } else {
          unsigned L = GetLocalLabel();
          AddCodeLine("and #0x80");
          AddCodeLine("jz %s", LocalLabelName(L));
          AddCodeLine("lda #0xff");
          g_defcodelabel(L);
          AddCodeLine("sta A,#1");
        }
        break;
      }
    case CF_INT:
      AddCodeLine("lda %s", lbuf);
      AddCodeLine("add A");
      AddCodeLine("sta %s", lbuf);
      AddCodeLine("sta A");
      AddCodeLine("lda %s,#1", lbuf);
      AddCodeLine("add A,#1");
      AddCodeLine("sta %s,#1", lbuf);
      AddCodeLine("sta A,#1");
      break;
    case CF_LONG:
      AddCodeLine("lda %s", lbuf);
      AddCodeLine("add A");
      AddCodeLine("sta %s", lbuf);
      AddCodeLine("sta A");
      AddCodeLine("lda %s,#1", lbuf);
      AddCodeLine("add A,#1");
      AddCodeLine("sta %s,#1", lbuf);
      AddCodeLine("sta A,#1");
      AddCodeLine("lda %s,#2", lbuf);
      AddCodeLine("add A,#2");
      AddCodeLine("sta %s,#2", lbuf);
      AddCodeLine("sta A,#2");
      AddCodeLine("lda %s,#3", lbuf);
      AddCodeLine("add A,#3");
      AddCodeLine("sta %s,#3", lbuf);
      AddCodeLine("sta A,#3");
      break;
    default:
      typeerror(flags);
    }
  }
}

void g_addeqlocal(unsigned flags, int offs, unsigned long val)
/* Emit += for a local variable */
{
  AddCodeLine_("debug %s", __FUNCTION__);
  /* Calculate the true offset, check it, load it into Y */
  offs -= StackPtr;
  CheckLocalOffs(offs);

  AddCodeLine("clc");
  if (flags & CF_CONST) {
    switch (flags & CF_TYPEMASK) {
    case CF_CHAR:
      if (flags & CF_FORCECHAR) {
        AddCodeLine("lda (SP),#%d", offs);
        AddCodeLine("add #%d", (unsigned char)val);
        AddCodeLine("sta (SP),#%d", offs);
        AddCodeLine("sta A");
        if (flags & CF_UNSIGNED) {
          AddCodeLine("lda #0");
          AddCodeLine("sta A,#1");
        } else {
          unsigned L = GetLocalLabel();
          AddCodeLine("and #0x80");
          AddCodeLine("jz %s", LocalLabelName(L));
          AddCodeLine("lda #0xff");
          g_defcodelabel(L);
          AddCodeLine("sta A,#1");
        }
        break;
      }
    case CF_INT:
      AddCodeLine("lda (SP),#%d", offs);
      AddCodeLine("add #%d", (unsigned char)val);
      AddCodeLine("sta (SP),#%d", offs);
      AddCodeLine("sta A");
      AddCodeLine("lda (SP),#%d", offs + 1);
      AddCodeLine("add #%d", (unsigned char)(val >> 8));
      AddCodeLine("sta (SP),#%d", offs + 1);
      AddCodeLine("sta A,#1");
      break;
    case CF_LONG:
      AddCodeLine("lda (SP),#%d", offs);
      AddCodeLine("add #%d", (unsigned char)val);
      AddCodeLine("sta (SP),#%d", offs);
      AddCodeLine("sta A");
      AddCodeLine("lda (SP),#%d", offs + 1);
      AddCodeLine("add #%d", (unsigned char)(val >> 8));
      AddCodeLine("sta (SP),#%d", offs + 1);
      AddCodeLine("sta A,#1");
      AddCodeLine("lda (SP),#%d", offs + 2);
      AddCodeLine("add #%d", (unsigned char)(val >> 16));
      AddCodeLine("sta (SP),#%d", offs + 2);
      AddCodeLine("sta A,#2");
      AddCodeLine("lda (SP),#%d", offs + 3);
      AddCodeLine("add #%d", (unsigned char)(val >> 24));
      AddCodeLine("sta (SP),#%d", offs + 3);
      AddCodeLine("sta A,#3");
      break;
    default:
      typeerror(flags);
    }
  } else {
    switch (flags & CF_TYPEMASK) {
    case CF_CHAR:
      if (flags & CF_FORCECHAR) {
        AddCodeLine("lda (SP),#%d", offs);
        AddCodeLine("add A");
        AddCodeLine("sta (SP),#%d", offs);
        AddCodeLine("sta A");
        if (flags & CF_UNSIGNED) {
          AddCodeLine("lda #0");
          AddCodeLine("sta A,#1");
        } else {
          unsigned L = GetLocalLabel();
          AddCodeLine("and #0x80");
          AddCodeLine("jz %s", LocalLabelName(L));
          AddCodeLine("lda #0xff");
          g_defcodelabel(L);
          AddCodeLine("sta A,#1");
        }
        break;
      }
    case CF_INT:
      AddCodeLine("lda (SP),#%d", offs);
      AddCodeLine("add A");
      AddCodeLine("sta (SP),#%d", offs);
      AddCodeLine("sta A");
      AddCodeLine("lda (SP),#%d", offs + 1);
      AddCodeLine("add A,#1");
      AddCodeLine("sta (SP),#%d", offs + 1);
      AddCodeLine("sta A,#1");
      break;
    case CF_LONG:
      AddCodeLine("lda (SP),#%d", offs);
      AddCodeLine("add A");
      AddCodeLine("sta (SP),#%d", offs);
      AddCodeLine("sta A");
      AddCodeLine("lda (SP),#%d", offs + 1);
      AddCodeLine("add A,#1");
      AddCodeLine("sta (SP),#%d", offs + 1);
      AddCodeLine("sta A,#1");
      AddCodeLine("lda (SP),#%d", offs + 2);
      AddCodeLine("add A,#2");
      AddCodeLine("sta (SP),#%d", offs + 2);
      AddCodeLine("sta A,#2");
      AddCodeLine("lda (SP),#%d", offs + 3);
      AddCodeLine("add A,#3");
      AddCodeLine("sta (SP),#%d", offs + 3);
      AddCodeLine("sta A,#3");
      break;
    default:
      typeerror(flags);
    }
  }
}

void g_addeqind(unsigned flags, unsigned offs, unsigned long val)
/* Emit += for the location with address in ax */
{
  AddCodeLine_("debug %s", __FUNCTION__);
  /* If the offset is too large for a byte register, add the high byte
  ** of the offset to the primary. Beware: We need a special correction
  ** if the offset in the low byte will overflow in the operation.
  */
  offs = MakeByteOffs(flags, offs);

  AddCodeLine("clc");
  switch (flags & CF_TYPEMASK) {
  case CF_CHAR:
    AddCodeLine("lda (A),#%d", offs);
    AddCodeLine("add #%d", (unsigned char)val);
    AddCodeLine("sta (A), offs");
    break;
  case CF_INT:
    AddCodeLine("lda (A),#%d", offs);
    AddCodeLine("add #%d", (unsigned char)val);
    AddCodeLine("sta (A),#%d", offs);
    AddCodeLine("lda (A),#%d", offs + 1);
    AddCodeLine("add #%d", (unsigned char)(val >> 8));
    AddCodeLine("sta (A),#%d", offs + 1);
    break;
  case CF_LONG:
    AddCodeLine("lda (A),#%d", offs);
    AddCodeLine("add #%d", (unsigned char)val);
    AddCodeLine("sta (A),#%d", offs);
    AddCodeLine("lda (A),#%d", offs + 1);
    AddCodeLine("add #%d", (unsigned char)(val >> 8));
    AddCodeLine("sta (A),#%d", offs + 1);
    AddCodeLine("lda (A),#%d", offs + 2);
    AddCodeLine("add #%d", (unsigned char)(val >> 16));
    AddCodeLine("sta (A),#%d", offs + 2);
    AddCodeLine("lda (A),#%d", offs + 3);
    AddCodeLine("add #%d", (unsigned char)(val >> 24));
    AddCodeLine("sta (A),#%d", offs + 3);
    break;
  default:
    typeerror(flags);
  }
}

void g_subeqstatic(unsigned flags, uintptr_t label, long offs, unsigned long val)
/* Emit -= for a static variable */
{
  AddCodeLine_("debug %s", __FUNCTION__);
  /* Create the correct label name */
  const char *lbuf = GetLabelName(flags, label, offs);

  AddCodeLine("sec");
  if (flags & CF_CONST) {
    switch (flags & CF_TYPEMASK) {
    case CF_CHAR:
      if (flags & CF_FORCECHAR) {
        AddCodeLine("lda %s", lbuf);
        AddCodeLine("sub #%d", (unsigned char)val);
        AddCodeLine("sta %s", lbuf);
        if (flags & CF_UNSIGNED) {
          AddCodeLine("lda #0");
          AddCodeLine("sta A,#1");
        } else {
          unsigned L = GetLocalLabel();
          AddCodeLine("and #0x80");
          AddCodeLine("jz %s", LocalLabelName(L));
          AddCodeLine("lda #0xff");
          g_defcodelabel(L);
          AddCodeLine("sta A,#1");
        }
        break;
      }
    case CF_INT:
      AddCodeLine("lda %s", lbuf);
      AddCodeLine("sub #%d", (unsigned char)val);
      AddCodeLine("sta %s", lbuf);
      AddCodeLine("lda %s,#1", lbuf);
      AddCodeLine("sub #%d", (unsigned char)(val >> 8));
      AddCodeLine("sta %s,#1", lbuf);
      break;
    case CF_LONG:
      AddCodeLine("lda %s", lbuf);
      AddCodeLine("sub #%d", (unsigned char)val);
      AddCodeLine("sta %s", lbuf);
      AddCodeLine("lda %s,#1", lbuf);
      AddCodeLine("sub #%d", (unsigned char)(val >> 8));
      AddCodeLine("sta %s,#1", lbuf);
      AddCodeLine("lda %s,#2", lbuf);
      AddCodeLine("sub #%d", (unsigned char)(val >> 16));
      AddCodeLine("sta %s,#2", lbuf);
      AddCodeLine("lda %s,#3", lbuf);
      AddCodeLine("sub #%d", (unsigned char)(val >> 24));
      AddCodeLine("sta %s,#3", lbuf);
      break;
    default:
      typeerror(flags);
    }
  } else {
    switch (flags & CF_TYPEMASK) {
    case CF_CHAR:
      if (flags & CF_FORCECHAR) {
        AddCodeLine("lda %s", lbuf);
        AddCodeLine("sub A");
        AddCodeLine("sta %s", lbuf);
        if (flags & CF_UNSIGNED) {
          AddCodeLine("lda #0");
          AddCodeLine("sta A,#1");
        } else {
          unsigned L = GetLocalLabel();
          AddCodeLine("and #0x80");
          AddCodeLine("jz %s", LocalLabelName(L));
          AddCodeLine("lda #0xff");
          g_defcodelabel(L);
          AddCodeLine("sta A,#1");
        }
        break;
      }
    case CF_INT:
      AddCodeLine("lda %s", lbuf);
      AddCodeLine("sub A");
      AddCodeLine("sta %s", lbuf);
      AddCodeLine("lda %s,#1", lbuf);
      AddCodeLine("sub A,#1");
      AddCodeLine("sta %s,#1", lbuf);
      break;
    case CF_LONG:
      AddCodeLine("lda %s", lbuf);
      AddCodeLine("sub A");
      AddCodeLine("sta %s", lbuf);
      AddCodeLine("lda %s,#1", lbuf);
      AddCodeLine("sub A,#1");
      AddCodeLine("sta %s,#1", lbuf);
      AddCodeLine("lda %s,#2", lbuf);
      AddCodeLine("sub A,#2");
      AddCodeLine("sta %s,#2", lbuf);
      AddCodeLine("lda %s,#3", lbuf);
      AddCodeLine("sub A,#3");
      AddCodeLine("sta %s,#3", lbuf);
      break;
    default:
      typeerror(flags);
    }
  }
}

void g_subeqlocal(unsigned flags, int Offs, unsigned long val)
/* Emit -= for a local variable */
{
  AddCodeLine_("debug %s", __FUNCTION__);
  /* Calculate the true offset, check it, load it into Y */
  Offs -= StackPtr;
  CheckLocalOffs(Offs);

  AddCodeLine("sec");
  if (flags & CF_CONST) {
    switch (flags & CF_TYPEMASK) {
    case CF_CHAR:
      if (flags & CF_FORCECHAR) {
        AddCodeLine("lda (SP),#%d", Offs);
        AddCodeLine("sub #%d", (unsigned char)val);
        AddCodeLine("sta (SP),#%d", Offs);
        AddCodeLine("sta A");
        if (flags & CF_UNSIGNED) {
          AddCodeLine("lda #0");
          AddCodeLine("sta A,#1");
        } else {
          unsigned L = GetLocalLabel();
          AddCodeLine("and #0x80");
          AddCodeLine("jz %s", LocalLabelName(L));
          AddCodeLine("lda #0xff");
          g_defcodelabel(L);
          AddCodeLine("sta A,#1");
        }
        break;
      }
    case CF_INT:
      AddCodeLine("lda (SP),#%d", Offs);
      AddCodeLine("sub #%d", (unsigned char)val);
      AddCodeLine("sta (SP),#%d", Offs);
      AddCodeLine("sta A");
      AddCodeLine("lda (SP),#%d", Offs + 1);
      AddCodeLine("sub #%d", (unsigned char)(val >> 8));
      AddCodeLine("sta (SP),#%d", Offs + 1);
      AddCodeLine("sta A,#1");
      break;
    case CF_LONG:
      AddCodeLine("lda (SP),#%d", Offs);
      AddCodeLine("sub #%d", (unsigned char)val);
      AddCodeLine("sta (SP),#%d", Offs);
      AddCodeLine("sta A");
      AddCodeLine("lda (SP),#%d", Offs + 1);
      AddCodeLine("sub #%d", (unsigned char)(val >> 8));
      AddCodeLine("sta (SP),#%d", Offs + 1);
      AddCodeLine("sta A,#1");
      AddCodeLine("lda (SP),#%d", Offs + 2);
      AddCodeLine("sub #%d", (unsigned char)(val >> 16));
      AddCodeLine("sta (SP),#%d", Offs + 2);
      AddCodeLine("sta A,#2");
      AddCodeLine("lda (SP),#%d", Offs + 3);
      AddCodeLine("sub #%d", (unsigned char)(val >> 24));
      AddCodeLine("sta (SP),#%d", Offs + 3);
      AddCodeLine("sta A,#3");
      break;
    default:
      typeerror(flags);
    }
  } else {
    switch (flags & CF_TYPEMASK) {
    case CF_CHAR:
      if (flags & CF_FORCECHAR) {
        AddCodeLine("lda (SP),#%d", Offs);
        AddCodeLine("sub A");
        AddCodeLine("sta (SP),#%d", Offs);
        AddCodeLine("sta A");
        if (flags & CF_UNSIGNED) {
          AddCodeLine("lda #0");
          AddCodeLine("sta A,#1");
        } else {
          unsigned L = GetLocalLabel();
          AddCodeLine("and #0x80");
          AddCodeLine("jz %s", LocalLabelName(L));
          AddCodeLine("lda #0xff");
          g_defcodelabel(L);
          AddCodeLine("sta A,#1");
        }
        break;
      }
    case CF_INT:
      AddCodeLine("lda (SP),#%d", Offs);
      AddCodeLine("sub A");
      AddCodeLine("sta (SP),#%d", Offs);
      AddCodeLine("sta A");
      AddCodeLine("lda (SP),#%d", Offs + 1);
      AddCodeLine("sub A,#1");
      AddCodeLine("sta (SP),#%d", Offs + 1);
      AddCodeLine("sta A,#1");
      break;
    case CF_LONG:
      AddCodeLine("lda (SP),#%d", Offs);
      AddCodeLine("sub A");
      AddCodeLine("sta (SP),#%d", Offs);
      AddCodeLine("sta A");
      AddCodeLine("lda (SP),#%d", Offs + 1);
      AddCodeLine("sub A,#1");
      AddCodeLine("sta (SP),#%d", Offs + 1);
      AddCodeLine("sta A,#1");
      AddCodeLine("lda (SP),#%d", Offs + 2);
      AddCodeLine("sub A,#2");
      AddCodeLine("sta (SP),#%d", Offs + 2);
      AddCodeLine("sta A,#2");
      AddCodeLine("lda (SP),#%d", Offs + 3);
      AddCodeLine("sub A,#3");
      AddCodeLine("sta (SP),#%d", Offs + 3);
      AddCodeLine("sta A,#3");
      break;
    default:
      typeerror(flags);
    }
  }
}

void g_subeqind(unsigned flags, unsigned offs, unsigned long val)
/* Emit -= for the location with address in ax */
{
  AddCodeLine_("debug %s", __FUNCTION__);
  /* If the offset is too large for a byte register, add the high byte
  ** of the offset to the primary. Beware: We need a special correction
  ** if the offset in the low byte will overflow in the operation.
  */
  offs = MakeByteOffs(flags, offs);

  AddCodeLine("sec");
  switch (flags & CF_TYPEMASK) {
  case CF_CHAR:
    AddCodeLine("lda (A),#%d", offs);
    AddCodeLine("sub #%d", (unsigned char)val);
    AddCodeLine("sta (A), offs");
    break;
  case CF_INT:
    AddCodeLine("lda (A),#%d", offs);
    AddCodeLine("sub #%d", (unsigned char)val);
    AddCodeLine("sta (A),#%d", offs);
    AddCodeLine("lda (A),#%d", offs + 1);
    AddCodeLine("sub #%d", (unsigned char)(val >> 8));
    AddCodeLine("sta (A),#%d", offs + 1);
    break;
  case CF_LONG:
    AddCodeLine("lda (A),#%d", offs);
    AddCodeLine("sub #%d", (unsigned char)val);
    AddCodeLine("sta (A),#%d", offs);
    AddCodeLine("lda (A),#%d", offs + 1);
    AddCodeLine("sub #%d", (unsigned char)(val >> 8));
    AddCodeLine("sta (A),#%d", offs + 1);
    AddCodeLine("lda (A),#%d", offs + 2);
    AddCodeLine("sub #%d", (unsigned char)(val >> 16));
    AddCodeLine("sta (A),#%d", offs + 2);
    AddCodeLine("lda (A),#%d", offs + 3);
    AddCodeLine("sub #%d", (unsigned char)(val >> 24));
    AddCodeLine("sta (A),#%d", offs + 3);
    break;
  default:
    typeerror(flags);
  }
}

/*****************************************************************************/
/*                 Add a variable address to the value in ax                 */
/*****************************************************************************/

void g_addaddr_local(unsigned flags attribute((unused)), int offs)
/* Add the address of a local variable to ax */
{
  AddCodeLine_("debug %s", __FUNCTION__);

  /* Add the offset */
  offs -= StackPtr;
  if (offs != 0) {
    /* We cannot address more then 256 bytes of locals anyway */
    CheckLocalOffs(offs);
    AddCodeLine("clc");
    AddCodeLine("lda A");
    AddCodeLine("add #%d", offs & 0xff);
    AddCodeLine("sta A");
    AddCodeLine("lda A,#1");
    AddCodeLine("add #0");
    AddCodeLine("sta A,#1");
  }

  /* Add the current stackpointer value */
  AddCodeLine("clc");
  AddCodeLine("lda A");
  AddCodeLine("add SP");
  AddCodeLine("sta A");
  AddCodeLine("lda A,#1");
  AddCodeLine("add SP,#1");
  AddCodeLine("sta A,#1");
}

void g_addaddr_static(unsigned flags, uintptr_t label, long offs)
/* Add the address of a static variable to ax */
{
  AddCodeLine_("debug %s", __FUNCTION__);
  /* Create the correct label name */
  const char *lbuf = GetLabelName(flags, label, offs);

  AddCodeLine("clc");
  AddCodeLine("lda A");
  AddCodeLine("add #L(%s)", lbuf);
  AddCodeLine("sta A");
  AddCodeLine("lda A,#1");
  AddCodeLine("add #H(%s)", lbuf);
  AddCodeLine("sta A,#1");
}

void g_add(unsigned flags, unsigned long val) {
  AddCodeLine_("debug %s", __FUNCTION__);

  AddCodeLine("clc");
  if (flags & CF_CONST) {
    switch (flags & CF_TYPEMASK) {
    case CF_CHAR:
      if (flags & CF_FORCECHAR) {
        AddCodeLine("lda A");
        AddCodeLine("add #%d", (unsigned char)val);
        AddCodeLine("sta A");
        break;
      }
      // FALL THROUGH
    case CF_INT:
      AddCodeLine("lda A");
      AddCodeLine("add #%d", (unsigned char)val);
      AddCodeLine("sta A");
      AddCodeLine("lda A,#1");
      AddCodeLine("add #%d", (unsigned char)(val >> 8));
      AddCodeLine("sta A,#1");
      break;
    case CF_LONG:
      AddCodeLine("lda A");
      AddCodeLine("add #%d", (unsigned char)val);
      AddCodeLine("sta A");
      AddCodeLine("lda A,#1");
      AddCodeLine("add #%d", (unsigned char)(val >> 8));
      AddCodeLine("sta A,#1");
      AddCodeLine("lda A,#2");
      AddCodeLine("add #%d", (unsigned char)(val >> 16));
      AddCodeLine("sta A,#2");
      AddCodeLine("lda A,#3");
      AddCodeLine("add #%d", (unsigned char)(val >> 24));
      AddCodeLine("sta A,#3");
      break;
    default:
      typeerror(flags);
    }
  } else {
    switch (flags & CF_TYPEMASK) {
    case CF_CHAR:
      if (flags & CF_FORCECHAR) {
        AddCodeLine("lda (SP)");
        AddCodeLine("add A");
        AddCodeLine("sta A");
        break;
      }
      // FALL THROUGH
    case CF_INT:
      AddCodeLine("lda (SP)");
      AddCodeLine("add A");
      AddCodeLine("sta A");
      AddCodeLine("lda (SP),#1");
      AddCodeLine("add A,#1");
      AddCodeLine("sta A,#1");
      break;
    case CF_LONG:
      AddCodeLine("lda (SP)");
      AddCodeLine("add A");
      AddCodeLine("sta A");
      AddCodeLine("lda (SP),#1");
      AddCodeLine("add A,#1");
      AddCodeLine("sta A,#1");
      AddCodeLine("lda (SP),#2");
      AddCodeLine("add A,#2");
      AddCodeLine("sta A,#2");
      AddCodeLine("lda (SP),#3");
      AddCodeLine("add A,#3");
      AddCodeLine("sta A,#3");
      break;
    default:
      typeerror(flags);
    }
  }

  if (!(flags & CF_CONST)) {
    g_drop(sizeofarg(flags));
    pop(flags);
  }
}

void g_sub(unsigned flags, unsigned long val) {
  AddCodeLine_("debug %s", __FUNCTION__);

  AddCodeLine("sec");
  if (flags & CF_CONST) {
    switch (flags & CF_TYPEMASK) {
    case CF_CHAR:
      if (flags & CF_FORCECHAR) {
        AddCodeLine("lda A");
        AddCodeLine("sub #%d", (unsigned char)val);
        AddCodeLine("sta A");
        break;
      }
      // FALL THROUGH
    case CF_INT:
      AddCodeLine("lda A");
      AddCodeLine("sub #%d", (unsigned char)val);
      AddCodeLine("sta A");
      AddCodeLine("lda A,#1");
      AddCodeLine("sub #%d", (unsigned char)(val >> 8));
      AddCodeLine("sta A,#1");
      break;
    case CF_LONG:
      AddCodeLine("lda A");
      AddCodeLine("sub #%d", (unsigned char)val);
      AddCodeLine("sta A");
      AddCodeLine("lda A,#1");
      AddCodeLine("sub #%d", (unsigned char)(val >> 8));
      AddCodeLine("sta A,#1");
      AddCodeLine("lda A,#2");
      AddCodeLine("sub #%d", (unsigned char)(val >> 16));
      AddCodeLine("sta A,#2");
      AddCodeLine("lda A,#3");
      AddCodeLine("sub #%d", (unsigned char)(val >> 24));
      AddCodeLine("sta A,#3");
      break;
    default:
      typeerror(flags);
    }
  } else {
    switch (flags & CF_TYPEMASK) {
    case CF_CHAR:
      if (flags & CF_FORCECHAR) {
        AddCodeLine("lda (SP)");
        AddCodeLine("sub A");
        AddCodeLine("sta A");
        break;
      }
      // FALL THROUGH
    case CF_INT:
      AddCodeLine("lda (SP)");
      AddCodeLine("sub A");
      AddCodeLine("sta A");
      AddCodeLine("lda (SP),#1");
      AddCodeLine("sub A,#1");
      AddCodeLine("sta A,#1");
      break;
    case CF_LONG:
      AddCodeLine("lda (SP)");
      AddCodeLine("sub A");
      AddCodeLine("sta A");
      AddCodeLine("lda (SP),#1");
      AddCodeLine("sub A,#1");
      AddCodeLine("sta A,#1");
      AddCodeLine("lda (SP),#2");
      AddCodeLine("sub A,#2");
      AddCodeLine("sta A,#2");
      AddCodeLine("lda (SP),#3");
      AddCodeLine("sub A,#3");
      AddCodeLine("sta A,#3");
      break;
    default:
      typeerror(flags);
    }
  }

  if (!(flags & CF_CONST)) {
    g_drop(sizeofarg(flags));
    pop(flags);
  }
}

void g_rsub(unsigned flags, unsigned long val)
/* Primary = Primary - TOS */
{
  AddCodeLine_("debug %s", __FUNCTION__);
  printf("> TODO %s\n", __FUNCTION__);
}

static void i2u_char(unsigned flags, unsigned long *val) {
  AddCodeLine_("debug %s", __FUNCTION__);
  unsigned L0 = GetLocalLabel();
  unsigned L1 = GetLocalLabel();

  if (flags & CF_CONST) {
    AddCodeLine("lda A");
    AddCodeLine("and #0x80");
    AddCodeLine("sta TMP4");
    AddCodeLine("jz %s", LocalLabelName(L0));

    // signed to unsigned
    AddCodeLine("clc");
    AddCodeLine("lda A");
    AddCodeLine("xor #0xff");
    AddCodeLine("add #1");
    AddCodeLine("sta A");

    g_defcodelabel(L0);

    AddCodeLine("lda TMP4");
    AddCodeLine("xor #%d", (unsigned char)((*val) & 0x80));
    AddCodeLine("sta TMP4");
    if ((*val) & 0x80) {
      (*val) = !(*val) + 1;
    }
  } else {
    AddCodeLine("lda (SP)");
    AddCodeLine("and #0x80");
    AddCodeLine("sta TMP4");
    AddCodeLine("jz %s", LocalLabelName(L0));

    // signed to unsigned
    AddCodeLine("clc");
    AddCodeLine("lda (SP)");
    AddCodeLine("xor #0xff");
    AddCodeLine("add #1");
    AddCodeLine("sta (SP)");

    g_defcodelabel(L0);

    AddCodeLine("lda A");
    AddCodeLine("and #0x80");
    AddCodeLine("xor TMP4");
    AddCodeLine("sta TMP4");

    AddCodeLine("lda A");
    AddCodeLine("and #0x80");
    AddCodeLine("jz %s", LocalLabelName(L1));

    // signed to unsigned
    AddCodeLine("clc");
    AddCodeLine("lda A");
    AddCodeLine("xor #0xff");
    AddCodeLine("add #1");
    AddCodeLine("sta A");

    g_defcodelabel(L1);
  }
}

static void i2u_int(unsigned flags, unsigned long *val) {
  AddCodeLine_("debug %s", __FUNCTION__);
  unsigned L0 = GetLocalLabel();
  unsigned L1 = GetLocalLabel();

  if (flags & CF_CONST) {
    AddCodeLine("lda A,#1");
    AddCodeLine("and #0x80");
    AddCodeLine("sta TMP4");
    AddCodeLine("jz %s", LocalLabelName(L0));

    // signed to unsigned
    AddCodeLine("clc");
    AddCodeLine("lda A");
    AddCodeLine("xor #0xff");
    AddCodeLine("add #1");
    AddCodeLine("sta A");
    AddCodeLine("lda A,#1");
    AddCodeLine("xor #0xff");
    AddCodeLine("add #0");
    AddCodeLine("sta A,#1");

    g_defcodelabel(L0);

    AddCodeLine("lda TMP4");
    AddCodeLine("xor #%d", (unsigned char)((*val >> 8) & 0x80));
    AddCodeLine("sta TMP4");
    if ((*val) & 0x8000) {
      (*val) = !(*val) + 1;
    }
  } else {
    AddCodeLine("lda (SP),#1");
    AddCodeLine("and #0x80");
    AddCodeLine("sta TMP4");
    AddCodeLine("jz %s", LocalLabelName(L0));

    // signed to unsigned
    AddCodeLine("clc");
    AddCodeLine("lda (SP)");
    AddCodeLine("xor #0xff");
    AddCodeLine("add #1");
    AddCodeLine("sta (SP)");
    AddCodeLine("lda (SP),#1");
    AddCodeLine("xor #0xff");
    AddCodeLine("add #0");
    AddCodeLine("sta (SP),#1");

    g_defcodelabel(L0);

    AddCodeLine("lda A,#1");
    AddCodeLine("and #0x80");
    AddCodeLine("xor TMP4");
    AddCodeLine("sta TMP4");

    AddCodeLine("lda A,#1");
    AddCodeLine("and #0x80");
    AddCodeLine("jz %s", LocalLabelName(L1));

    // signed to unsigned
    AddCodeLine("clc");
    AddCodeLine("lda A");
    AddCodeLine("xor #0xff");
    AddCodeLine("add #1");
    AddCodeLine("sta A");
    AddCodeLine("lda A,#1");
    AddCodeLine("xor #0xff");
    AddCodeLine("add #0");
    AddCodeLine("sta A,#1");

    g_defcodelabel(L1);
  }
}

static void i2u_long(unsigned flags, unsigned long *val) {
  AddCodeLine_("debug %s", __FUNCTION__);
  unsigned L0 = GetLocalLabel();
  unsigned L1 = GetLocalLabel();

  if (flags & CF_CONST) {
    printf("> TODO i2u32_long const\n");
  } else {
    AddCodeLine("lda (SP),#3");
    AddCodeLine("and #0x80");
    AddCodeLine("sta TMP4");
    AddCodeLine("jz %s", LocalLabelName(L0));

    // signed to unsigned
    AddCodeLine("clc");
    AddCodeLine("lda (SP)");
    AddCodeLine("xor #0xff");
    AddCodeLine("add #1");
    AddCodeLine("sta (SP)");
    AddCodeLine("lda (SP),#1");
    AddCodeLine("xor #0xff");
    AddCodeLine("add #0");
    AddCodeLine("sta (SP),#1");
    AddCodeLine("lda (SP),#2");
    AddCodeLine("xor #0xff");
    AddCodeLine("add #0");
    AddCodeLine("sta (SP),#2");
    AddCodeLine("lda (SP),#3");
    AddCodeLine("xor #0xff");
    AddCodeLine("add #0");
    AddCodeLine("sta (SP),#3");

    g_defcodelabel(L0);

    AddCodeLine("lda A,#3");
    AddCodeLine("and #0x80");
    AddCodeLine("xor TMP4");
    AddCodeLine("sta TMP4");

    AddCodeLine("lda A,#3");
    AddCodeLine("and #0x80");
    AddCodeLine("jz %s", LocalLabelName(L1));

    // signed to unsigned
    AddCodeLine("clc");
    AddCodeLine("lda A");
    AddCodeLine("xor #0xff");
    AddCodeLine("add #1");
    AddCodeLine("sta A");
    AddCodeLine("lda A,#1");
    AddCodeLine("xor #0xff");
    AddCodeLine("add #0");
    AddCodeLine("sta A,#1");
    AddCodeLine("lda A,#2");
    AddCodeLine("xor #0xff");
    AddCodeLine("add #0");
    AddCodeLine("sta A,#2");
    AddCodeLine("lda A,#3");
    AddCodeLine("xor #0xff");
    AddCodeLine("add #0");
    AddCodeLine("sta A,#3");

    g_defcodelabel(L1);
  }
}

// signed to unsigned, TMP4: result sign
static void i2u(unsigned flags, unsigned long *val) {
  AddCodeLine_("debug %s", __FUNCTION__);
  // 1. signed to unsigned
  switch (flags & CF_TYPEMASK) {
  case CF_CHAR:
    if (flags & CF_FORCECHAR) {
      i2u_char(flags, val);
      break;
    }
    // FALL THROUGH
  case CF_INT:
    i2u_int(flags, val);
    break;
  case CF_LONG:
    i2u_long(flags, val);
    break;
  default:
    typeerror(flags);
  }
}

// A = TMP0 * TMP1
static void umul(unsigned flags, unsigned long val) {
  AddCodeLine_("debug %s", __FUNCTION__);
  if (flags & CF_CONST) {
    switch (flags & CF_TYPEMASK) {
    case CF_CHAR:
      if (flags & CF_FORCECHAR) {
        AddCodeLine("lda A"); // TMP0 = A
        AddCodeLine("sta TMP0");
        AddCodeLine("lda #%d", (unsigned char)val); // TMP1 = val
        AddCodeLine("sta TMP1");
        AddCodeLine("lda #0"); // A = 0
        AddCodeLine("sta A");
        AddCodeLine("lda #8"); // TMP2 = 8
        AddCodeLine("sta TMP2");
        break;
      }
      // FALL THROUGH
    case CF_INT:
      AddCodeLine("lda A"); // TMP0 = A
      AddCodeLine("sta TMP0");
      AddCodeLine("lda A,#1");
      AddCodeLine("sta TMP0,#1");
      AddCodeLine("lda #%d", (unsigned char)val); // TMP1 = val
      AddCodeLine("sta TMP1");
      AddCodeLine("lda #%d", (unsigned char)(val >> 8)); // TMP1 = val
      AddCodeLine("sta TMP1,#1");
      AddCodeLine("lda #0"); // A = 0
      AddCodeLine("sta A");
      AddCodeLine("sta A,#1");
      AddCodeLine("lda #16"); // TMP2 = 16
      AddCodeLine("sta TMP2");
      break;
    case CF_LONG:
      AddCodeLine("lda A"); // TMP0 = A
      AddCodeLine("sta TMP0");
      AddCodeLine("lda A,#1");
      AddCodeLine("sta TMP0,#1");
      AddCodeLine("lda A,#2");
      AddCodeLine("sta TMP0,#2");
      AddCodeLine("lda A,#3");
      AddCodeLine("sta TMP0,#3");

      AddCodeLine("lda #%d", (unsigned char)val); // TMP1 = val
      AddCodeLine("sta TMP1");
      AddCodeLine("lda #%d", (unsigned char)(val >> 8));
      AddCodeLine("sta TMP1,#1");
      AddCodeLine("lda #%d", (unsigned char)(val >> 16));
      AddCodeLine("sta TMP1,#2");
      AddCodeLine("lda #%d", (unsigned char)(val >> 24));
      AddCodeLine("sta TMP1,#3");

      AddCodeLine("lda #0"); // A = 0
      AddCodeLine("sta A");
      AddCodeLine("sta A,#1");
      AddCodeLine("sta A,#2");
      AddCodeLine("sta A,#3");
      AddCodeLine("lda #32"); // TMP2 = 32
      AddCodeLine("sta TMP2");
      break;
    default:
      typeerror(flags);
    }
  } else {
    switch (flags & CF_TYPEMASK) {
    case CF_CHAR:
      if (flags & CF_FORCECHAR) {
        AddCodeLine("lda (SP)"); // TMP0 = TOS
        AddCodeLine("sta TMP0");
        AddCodeLine("lda A"); // TMP1 = A
        AddCodeLine("sta TMP1");
        AddCodeLine("lda #0"); // A = 0
        AddCodeLine("sta A");
        AddCodeLine("lda #8"); // TMP2 = 8
        AddCodeLine("sta TMP2");
        break;
      }

    case CF_INT:
      AddCodeLine("lda (SP)"); // TMP0 = TOS
      AddCodeLine("sta TMP0");
      AddCodeLine("lda (SP),#1");
      AddCodeLine("sta TMP0,#1");
      AddCodeLine("lda A"); // TMP1 = A
      AddCodeLine("sta TMP1");
      AddCodeLine("lda A,#1");
      AddCodeLine("sta TMP1,#1");
      AddCodeLine("lda #0"); // A = 0
      AddCodeLine("sta A");
      AddCodeLine("sta A,#1");
      AddCodeLine("lda #16"); // TMP2 = 16
      AddCodeLine("sta TMP2");
      break;
    case CF_LONG:
      AddCodeLine("lda (SP)"); // TMP0 = TOS
      AddCodeLine("sta TMP0");
      AddCodeLine("lda (SP),#1");
      AddCodeLine("sta TMP0,#1");
      AddCodeLine("lda (SP),#2");
      AddCodeLine("sta TMP0,#2");
      AddCodeLine("lda (SP),#3");
      AddCodeLine("sta TMP0,#3");
      AddCodeLine("lda A"); // TMP1 = A
      AddCodeLine("sta TMP1");
      AddCodeLine("lda A,#1");
      AddCodeLine("sta TMP1,#1");
      AddCodeLine("lda A,#2");
      AddCodeLine("sta TMP1,#2");
      AddCodeLine("lda A,#3");
      AddCodeLine("sta TMP1,#3");
      AddCodeLine("lda #0"); // A = 0
      AddCodeLine("sta A");
      AddCodeLine("sta A,#1");
      AddCodeLine("sta A,#2");
      AddCodeLine("sta A,#3");
      AddCodeLine("lda #32"); // TMP2 = 32
      AddCodeLine("sta TMP2");
      break;
    default:
      typeerror(flags);
    }
  }

  unsigned L0 = GetLocalLabel();
  unsigned L1 = GetLocalLabel();

  switch (flags & CF_TYPEMASK) {
  case CF_CHAR:
    if (flags & CF_FORCECHAR) {
      g_defcodelabel(L0);
      AddCodeLine("clc"); // TMP1 >>= 1
      AddCodeLine("lda TMP1");
      AddCodeLine("sr");
      AddCodeLine("sta TMP1");
      AddCodeLine("jnc %s", LocalLabelName(L1));
      AddCodeLine("clc"); // A += TMP0
      AddCodeLine("lda A");
      AddCodeLine("add TMP0");
      AddCodeLine("sta A");
      g_defcodelabel(L1);
      AddCodeLine("clc"); // TMP0 <<= 1
      AddCodeLine("lda TMP0");
      AddCodeLine("sl");
      AddCodeLine("sta TMP0");
      AddCodeLine("clc");
      AddCodeLine("lda TMP2"); // TMP2 -= 1
      AddCodeLine("sub #1");
      AddCodeLine("sta TMP2");
      AddCodeLine("jnz %s", LocalLabelName(L0));

      break;
    }
  case CF_INT:
    g_defcodelabel(L0);
    AddCodeLine("clc"); // TMP1 >>= 1
    AddCodeLine("lda TMP1,#1");
    AddCodeLine("sr");
    AddCodeLine("sta TMP1,#1");
    AddCodeLine("lda TMP1");
    AddCodeLine("sr");
    AddCodeLine("sta TMP1");
    AddCodeLine("jnc %s", LocalLabelName(L1));
    AddCodeLine("clc"); // A += TMP0
    AddCodeLine("lda A");
    AddCodeLine("add TMP0");
    AddCodeLine("sta A");
    AddCodeLine("lda A,#1");
    AddCodeLine("add TMP0,#1");
    AddCodeLine("sta A,#1");
    g_defcodelabel(L1);
    AddCodeLine("clc"); // TMP0 <<= 1
    AddCodeLine("lda TMP0");
    AddCodeLine("sl");
    AddCodeLine("sta TMP0");
    AddCodeLine("lda TMP0,#1");
    AddCodeLine("sl");
    AddCodeLine("sta TMP0,#1");
    AddCodeLine("clc");
    AddCodeLine("lda TMP2"); // TMP2 -= 1
    AddCodeLine("sub #1");
    AddCodeLine("sta TMP2");
    AddCodeLine("jnz %s", LocalLabelName(L0));

    break;

  case CF_LONG:
    g_defcodelabel(L0);
    AddCodeLine("clc"); // TMP1 >>= 1
    AddCodeLine("lda TMP1,#3");
    AddCodeLine("sr");
    AddCodeLine("sta TMP1,#3");
    AddCodeLine("lda TMP1,#2");
    AddCodeLine("sr");
    AddCodeLine("sta TMP1,#2");
    AddCodeLine("lda TMP1,#1");
    AddCodeLine("sr");
    AddCodeLine("sta TMP1,#1");
    AddCodeLine("lda TMP1");
    AddCodeLine("sr");
    AddCodeLine("sta TMP1");
    AddCodeLine("jnc %s", LocalLabelName(L1));
    AddCodeLine("clc"); // A += TMP0
    AddCodeLine("lda A");
    AddCodeLine("add TMP0");
    AddCodeLine("sta A");
    AddCodeLine("lda A,#1");
    AddCodeLine("add TMP0,#1");
    AddCodeLine("sta A,#1");
    AddCodeLine("lda A,#2");
    AddCodeLine("add TMP0,#2");
    AddCodeLine("sta A,#2");
    AddCodeLine("lda A,#3");
    AddCodeLine("add TMP0,#3");
    AddCodeLine("sta A,#3");
    g_defcodelabel(L1);
    AddCodeLine("clc"); // TMP0 <<= 1
    AddCodeLine("lda TMP0");
    AddCodeLine("sl");
    AddCodeLine("sta TMP0");
    AddCodeLine("lda TMP0,#1");
    AddCodeLine("sl");
    AddCodeLine("sta TMP0,#1");
    AddCodeLine("lda TMP0,#2");
    AddCodeLine("sl");
    AddCodeLine("sta TMP0,#2");
    AddCodeLine("lda TMP0,#3");
    AddCodeLine("sl");
    AddCodeLine("sta TMP0,#3");
    AddCodeLine("clc");
    AddCodeLine("lda TMP2"); // TMP2 -= 1
    AddCodeLine("sub #1");
    AddCodeLine("sta TMP2");
    AddCodeLine("jnz %s", LocalLabelName(L0));
    break;
  default:
    typeerror(flags);
  }
}

static void imul(unsigned flags, unsigned long val) {
  AddCodeLine_("debug %s", __FUNCTION__);
  unsigned L0 = GetLocalLabel();

  i2u(flags, &val);
  umul(flags, val);

  AddCodeLine("lda TMP4"); // 1-异号 0-同号
  AddCodeLine("jz %s", LocalLabelName(L0));

  switch (flags & CF_TYPEMASK) {
  case CF_CHAR:
    if (flags & CF_FORCECHAR) {
      AddCodeLine("clc");
      AddCodeLine("lda A");
      AddCodeLine("xor #0xff");
      AddCodeLine("add #1");
      AddCodeLine("sta A");
      break;
    }
  case CF_INT:
    AddCodeLine("clc");
    AddCodeLine("lda A");
    AddCodeLine("xor #0xff");
    AddCodeLine("add #1");
    AddCodeLine("sta A");
    AddCodeLine("lda A,#1");
    AddCodeLine("xor #0xff");
    AddCodeLine("add #0");
    AddCodeLine("sta A,#1");
    break;
  case CF_LONG:
    AddCodeLine("clc");
    AddCodeLine("lda A");
    AddCodeLine("xor #0xff");
    AddCodeLine("add #1");
    AddCodeLine("sta A");
    AddCodeLine("lda A,#1");
    AddCodeLine("xor #0xff");
    AddCodeLine("add #0");
    AddCodeLine("sta A,#1");
    AddCodeLine("lda A,#2");
    AddCodeLine("xor #0xff");
    AddCodeLine("add #0");
    AddCodeLine("sta A,#2");
    AddCodeLine("lda A,#3");
    AddCodeLine("xor #0xff");
    AddCodeLine("add #0");
    AddCodeLine("sta A,#3");
    break;
  default:
    typeerror(flags);
  }

  g_defcodelabel(L0);
}

void g_mul(unsigned flags, unsigned long val)
/* Primary = TOS * Primary */
{
  AddCodeLine_("debug %s", __FUNCTION__);
  int p2;
  /* Do strength reduction if the value is constant and a power of two */
  if (flags & CF_CONST && (p2 = PowerOf2(val)) >= 0) {
    /* Generate a shift instead */
    g_asl(flags, p2);
    return;
  }

  if (flags & CF_UNSIGNED) {
    umul(flags, val);
  } else {
    imul(flags, val);
  }

  if (!(flags & CF_CONST)) {
    pop(flags);
    g_drop(sizeofarg(flags));
  }
}

// TMP0 = TOS % A, TMP1 = TOS / A
static void udiv(unsigned flags, unsigned long val) {
  AddCodeLine_("debug %s", __FUNCTION__);
  unsigned L0 = GetLocalLabel();
  unsigned L1 = GetLocalLabel();
  unsigned L2 = GetLocalLabel();

  if (flags & CF_CONST) {
    switch (flags & CF_TYPEMASK) {
    case CF_CHAR:
      // TMP0: 2Byte, TMP1: 1Byte, TMP2: 2Byte, TMP3: 2Byte
      if (flags & CF_FORCECHAR) {
        AddCodeLine("lda #0"); // TMP0 = A, TMP1 = 0, TMP2 = val << 8
        AddCodeLine("sta TMP0,#1");
        AddCodeLine("sta TMP1");
        AddCodeLine("sta TMP2");
        AddCodeLine("lda A");
        AddCodeLine("sta TMP0");
        AddCodeLine("lda #%d", (unsigned char)val);
        AddCodeLine("sta TMP2,#1");
        AddCodeLine("lda #9"); // A = 9
        AddCodeLine("sta A");
        break;
      }

    case CF_INT:
      // TMP0: 4Byte, TMP1: 2Byte, TMP2: 4Byte, TMP3: 4Byte
      AddCodeLine("lda #0"); // TMP0 = A, TMP1 = 0, TMP2 = val << 16
      AddCodeLine("sta TMP0,#2");
      AddCodeLine("sta TMP0,#3");
      AddCodeLine("sta TMP2");
      AddCodeLine("sta TMP2,#1");
      AddCodeLine("sta TMP1");
      AddCodeLine("sta TMP1,#1");

      AddCodeLine("lda A");
      AddCodeLine("sta TMP0");
      AddCodeLine("lda A,#1");
      AddCodeLine("sta TMP0,#1");
      AddCodeLine("lda #%d", (unsigned char)val);
      AddCodeLine("sta TMP2,#2");
      AddCodeLine("lda #%d", (unsigned char)(val >> 8));
      AddCodeLine("sta TMP2,#3");
      AddCodeLine("lda #17"); // A = 17
      AddCodeLine("sta A");

      break;
    case CF_LONG:
      // TMP0: 8Byte, TMP1: 4Byte, TMP2: 8Byte, TMP3: 8Byte
      AddCodeLine("lda #0"); // TMP0 = A, TMP1 = 0, TMP2 = val << 32
      AddCodeLine("sta TMP0,#4");
      AddCodeLine("sta TMP0,#5");
      AddCodeLine("sta TMP0,#6");
      AddCodeLine("sta TMP0,#7");
      AddCodeLine("sta TMP1");
      AddCodeLine("sta TMP1,#1");
      AddCodeLine("sta TMP1,#2");
      AddCodeLine("sta TMP1,#3");
      AddCodeLine("sta TMP2");
      AddCodeLine("sta TMP2,#1");
      AddCodeLine("sta TMP2,#2");
      AddCodeLine("sta TMP2,#3");

      AddCodeLine("lda A");
      AddCodeLine("sta TMP0");
      AddCodeLine("lda A,#1");
      AddCodeLine("sta TMP0,#1");
      AddCodeLine("lda A,#2");
      AddCodeLine("sta TMP0,#2");
      AddCodeLine("lda A,#3");
      AddCodeLine("sta TMP0,#3");
      AddCodeLine("lda #%d", (unsigned char)val);
      AddCodeLine("sta TMP0,#4");
      AddCodeLine("lda #%d", (unsigned char)(val >> 8));
      AddCodeLine("sta TMP0,#5");
      AddCodeLine("lda #%d", (unsigned char)(val >> 16));
      AddCodeLine("sta TMP0,#6");
      AddCodeLine("lda #%d", (unsigned char)(val >> 24));
      AddCodeLine("sta TMP0,#7");

      AddCodeLine("lda #17"); // A = 33
      AddCodeLine("sta A");
      break;
    default:
      typeerror(flags);
    }
  } else {
    switch (flags & CF_TYPEMASK) {
    case CF_CHAR:
      // TMP0: 2Byte, TMP1: 1Byte, TMP2: 2Byte, TMP3: 2Byte
      if (flags & CF_FORCECHAR) {
        AddCodeLine("lda #0"); // TMP0 = TOS, TMP1 = 0, TMP2 = A << 8
        AddCodeLine("sta TMP0,#1");
        AddCodeLine("sta TMP1");
        AddCodeLine("sta TMP2");
        AddCodeLine("lda (SP)");
        AddCodeLine("sta TMP0");
        AddCodeLine("lda A");
        AddCodeLine("sta TMP2,#1");
        AddCodeLine("lda #9"); // A = 9
        AddCodeLine("sta A");
        break;
      }

    case CF_INT:
      // TMP0: 4Byte, TMP1: 2Byte, TMP2: 4Byte, TMP3: 4Byte
      AddCodeLine("lda #0"); // TMP0 = TOS, TMP1 = 0, TMP2 = A << 16
      AddCodeLine("sta TMP0,#2");
      AddCodeLine("sta TMP0,#3");
      AddCodeLine("sta TMP2");
      AddCodeLine("sta TMP2,#1");
      AddCodeLine("sta TMP1");
      AddCodeLine("sta TMP1,#1");
      AddCodeLine("lda (SP)");
      AddCodeLine("sta TMP0");
      AddCodeLine("lda (SP),#1");
      AddCodeLine("sta TMP0,#1");
      AddCodeLine("lda A");
      AddCodeLine("sta TMP2,#2");
      AddCodeLine("lda A,#1");
      AddCodeLine("sta TMP2,#3");
      AddCodeLine("lda #17"); // A = 17
      AddCodeLine("sta A");

      break;
    case CF_LONG:
      // TMP0: 8Byte, TMP1: 4Byte, TMP2: 8Byte, TMP3: 8Byte
      AddCodeLine("lda #0"); // TMP0 = TOS, TMP1 = 0, TMP2 = A << 32
      AddCodeLine("sta TMP0,#4");
      AddCodeLine("sta TMP0,#5");
      AddCodeLine("sta TMP0,#6");
      AddCodeLine("sta TMP0,#7");
      AddCodeLine("sta TMP1");
      AddCodeLine("sta TMP1,#1");
      AddCodeLine("sta TMP1,#2");
      AddCodeLine("sta TMP1,#3");
      AddCodeLine("sta TMP2");
      AddCodeLine("sta TMP2,#1");
      AddCodeLine("sta TMP2,#2");
      AddCodeLine("sta TMP2,#3");
      AddCodeLine("lda (SP)");
      AddCodeLine("sta TMP0");
      AddCodeLine("lda (SP),#1");
      AddCodeLine("sta TMP0,#1");
      AddCodeLine("lda (SP),#2");
      AddCodeLine("sta TMP0,#2");
      AddCodeLine("lda (SP),#3");
      AddCodeLine("sta TMP0,#3");
      AddCodeLine("lda A");
      AddCodeLine("sta TMP0,#4");
      AddCodeLine("lda A,#1");
      AddCodeLine("sta TMP0,#5");
      AddCodeLine("lda A,#2");
      AddCodeLine("sta TMP0,#6");
      AddCodeLine("lda A,#3");
      AddCodeLine("sta TMP0,#7");

      AddCodeLine("lda #17"); // A = 33
      AddCodeLine("sta A");
      break;
    default:
      typeerror(flags);
    }
  }

  switch (flags & CF_TYPEMASK) {
  case CF_CHAR:
    // TMP0: 2Byte, TMP1: 1Byte, TMP2: 2Byte, TMP3: 2Byte
    if (flags & CF_FORCECHAR) {
      g_defcodelabel(L0); // TMP0 / TMP2 start
      AddCodeLine("lda A");
      AddCodeLine("jz %s", LocalLabelName(L2));

      AddCodeLine("sec"); // TMP3 = TMP0 - TMP2
      AddCodeLine("lda TMP0");
      AddCodeLine("sub TMP2");
      AddCodeLine("sta TMP3");
      AddCodeLine("lda TMP0,#1");
      AddCodeLine("sub TMP2,#1");
      AddCodeLine("sta TMP3,#1");

      AddCodeLine("jnc %s", LocalLabelName(L1)); // if (无借位) TMP0 = TMP3
      AddCodeLine("lda TMP3");
      AddCodeLine("sta TMP0");
      AddCodeLine("lda TMP3,#1");
      AddCodeLine("sta TMP0,#1");
      g_defcodelabel(L1);

      AddCodeLine("lda TMP1"); // TMP1 = TMP1 << 1 + C
      AddCodeLine("sl");
      AddCodeLine("sta TMP1");

      AddCodeLine("clc"); // TMP2 = TMP2 >> 1
      AddCodeLine("lda TMP2,#1");
      AddCodeLine("sr");
      AddCodeLine("sta TMP2,#1");
      AddCodeLine("lda TMP2");
      AddCodeLine("sr");
      AddCodeLine("sta TMP2");

      AddCodeLine("sec"); // A = A - 1
      AddCodeLine("lda A");
      AddCodeLine("sub #1");
      AddCodeLine("sta A");
      AddCodeLine("jmp %s", LocalLabelName(L0));

      g_defcodelabel(L2);
      break;
    }

  case CF_INT:
    g_defcodelabel(L0); // TMP0 / TMP2 start
    AddCodeLine("lda A");
    AddCodeLine("jz %s", LocalLabelName(L2));

    AddCodeLine("sec"); // TMP3 = TMP0 - TMP2
    AddCodeLine("lda TMP0");
    AddCodeLine("sub TMP2");
    AddCodeLine("sta TMP3");
    AddCodeLine("lda TMP0,#1");
    AddCodeLine("sub TMP2,#1");
    AddCodeLine("sta TMP3,#1");
    AddCodeLine("lda TMP0,#2");
    AddCodeLine("sub TMP2,#2");
    AddCodeLine("sta TMP3,#2");
    AddCodeLine("lda TMP0,#3");
    AddCodeLine("sub TMP2,#3");
    AddCodeLine("sta TMP3,#3");

    AddCodeLine("jnc %s", LocalLabelName(L1)); // if (无借位) TMP0 = TMP3
    AddCodeLine("lda TMP3");
    AddCodeLine("sta TMP0");
    AddCodeLine("lda TMP3,#1");
    AddCodeLine("sta TMP0,#1");
    AddCodeLine("lda TMP3,#2");
    AddCodeLine("sta TMP0,#2");
    AddCodeLine("lda TMP3,#3");
    AddCodeLine("sta TMP0,#3");
    g_defcodelabel(L1);

    AddCodeLine("lda TMP1"); // TMP1 = TMP1 << 1 + C
    AddCodeLine("sl");
    AddCodeLine("sta TMP1");
    AddCodeLine("lda TMP1,#1");
    AddCodeLine("sl");
    AddCodeLine("sta TMP1,#1");

    AddCodeLine("clc"); // TMP2 = TMP2 >> 1
    AddCodeLine("lda TMP2,#3");
    AddCodeLine("sr");
    AddCodeLine("sta TMP2,#3");
    AddCodeLine("lda TMP2,#2");
    AddCodeLine("sr");
    AddCodeLine("sta TMP2,#2");
    AddCodeLine("lda TMP2,#1");
    AddCodeLine("sr");
    AddCodeLine("sta TMP2,#1");
    AddCodeLine("lda TMP2");
    AddCodeLine("sr");
    AddCodeLine("sta TMP2");

    AddCodeLine("sec"); // A = A - 1
    AddCodeLine("lda A");
    AddCodeLine("sub #1");
    AddCodeLine("sta A");
    AddCodeLine("jmp %s", LocalLabelName(L0));

    g_defcodelabel(L2);

    break;
  case CF_LONG:
    g_defcodelabel(L0); // TMP0 / TMP2 start
    AddCodeLine("lda A");
    AddCodeLine("jz %s", LocalLabelName(L2));

    AddCodeLine("sec"); // TMP3 = TMP0 - TMP2
    AddCodeLine("lda TMP0");
    AddCodeLine("sub TMP2");
    AddCodeLine("sta TMP3");
    AddCodeLine("lda TMP0,#1");
    AddCodeLine("sub TMP2,#1");
    AddCodeLine("sta TMP3,#1");
    AddCodeLine("lda TMP0,#2");
    AddCodeLine("sub TMP2,#2");
    AddCodeLine("sta TMP3,#2");
    AddCodeLine("lda TMP0,#3");
    AddCodeLine("sub TMP2,#3");
    AddCodeLine("sta TMP3,#3");
    AddCodeLine("lda TMP0,#4");
    AddCodeLine("sub TMP2,#4");
    AddCodeLine("sta TMP3,#4");
    AddCodeLine("lda TMP0,#5");
    AddCodeLine("sub TMP2,#5");
    AddCodeLine("sta TMP3,#5");
    AddCodeLine("lda TMP0,#6");
    AddCodeLine("sub TMP2,#6");
    AddCodeLine("sta TMP3,#6");
    AddCodeLine("lda TMP0,#7");
    AddCodeLine("sub TMP2,#7");
    AddCodeLine("sta TMP3,#7");

    AddCodeLine("jnc %s", LocalLabelName(L1)); // if (无借位) TMP0 = TMP3
    AddCodeLine("lda TMP3");
    AddCodeLine("sta TMP0");
    AddCodeLine("lda TMP3,#1");
    AddCodeLine("sta TMP0,#1");
    AddCodeLine("lda TMP3,#2");
    AddCodeLine("sta TMP0,#2");
    AddCodeLine("lda TMP3,#3");
    AddCodeLine("sta TMP0,#3");
    AddCodeLine("lda TMP3,#4");
    AddCodeLine("sta TMP0,#4");
    AddCodeLine("lda TMP3,#5");
    AddCodeLine("sta TMP0,#5");
    AddCodeLine("lda TMP3,#6");
    AddCodeLine("sta TMP0,#6");
    AddCodeLine("lda TMP3,#7");
    AddCodeLine("sta TMP0,#7");
    g_defcodelabel(L1);

    AddCodeLine("lda TMP1"); // TMP1 = TMP1 << 1 + C
    AddCodeLine("sl");
    AddCodeLine("sta TMP1");
    AddCodeLine("lda TMP1,#1");
    AddCodeLine("sl");
    AddCodeLine("sta TMP1,#1");
    AddCodeLine("lda TMP1,#2");
    AddCodeLine("sl");
    AddCodeLine("sta TMP1,#2");
    AddCodeLine("lda TMP1,#3");
    AddCodeLine("sl");
    AddCodeLine("sta TMP1,#3");

    AddCodeLine("clc"); // TMP2 = TMP2 >> 1
    AddCodeLine("lda TMP2,#7");
    AddCodeLine("sr");
    AddCodeLine("sta TMP2,#7");
    AddCodeLine("lda TMP2,#6");
    AddCodeLine("sr");
    AddCodeLine("sta TMP2,#6");
    AddCodeLine("lda TMP2,#5");
    AddCodeLine("sr");
    AddCodeLine("sta TMP2,#5");
    AddCodeLine("lda TMP2,#4");
    AddCodeLine("sr");
    AddCodeLine("sta TMP2,#4");
    AddCodeLine("lda TMP2,#3");
    AddCodeLine("sr");
    AddCodeLine("sta TMP2,#3");
    AddCodeLine("lda TMP2,#2");
    AddCodeLine("sr");
    AddCodeLine("sta TMP2,#2");
    AddCodeLine("lda TMP2,#1");
    AddCodeLine("sr");
    AddCodeLine("sta TMP2,#1");
    AddCodeLine("lda TMP2");
    AddCodeLine("sr");
    AddCodeLine("sta TMP2");

    AddCodeLine("sec"); // A = A - 1
    AddCodeLine("lda A");
    AddCodeLine("sub #1");
    AddCodeLine("sta A");
    AddCodeLine("jmp %s", LocalLabelName(L0));

    g_defcodelabel(L2);
    break;
  default:
    typeerror(flags);
  }

  if (!(flags & CF_CONST)) {
    pop(flags);
    g_drop(sizeofarg(flags));
  }
}

static void idiv(unsigned flags, unsigned long val) {
  AddCodeLine_("debug %s", __FUNCTION__);
  unsigned L0 = GetLocalLabel();
  i2u(flags, &val);
  udiv(flags, val);
  AddCodeLine("lda TMP4");
  AddCodeLine("jz %s", LocalLabelName(L0));

  switch (flags & CF_TYPEMASK) {
  case CF_CHAR:
    if (flags & CF_FORCECHAR) {
      AddCodeLine("clc");
      AddCodeLine("lda TMP1");
      AddCodeLine("xor #0xff");
      AddCodeLine("add #1");
      AddCodeLine("sta TMP1");
      break;
    }
  case CF_INT:
    AddCodeLine("clc");
    AddCodeLine("lda TMP1");
    AddCodeLine("xor #0xff");
    AddCodeLine("add #1");
    AddCodeLine("sta TMP1");
    AddCodeLine("lda TMP1,#1");
    AddCodeLine("xor #0xff");
    AddCodeLine("add #0");
    AddCodeLine("sta TMP1,#1");
    break;
  case CF_LONG:
    AddCodeLine("clc");
    AddCodeLine("lda TMP1");
    AddCodeLine("xor #0xff");
    AddCodeLine("add #1");
    AddCodeLine("sta TMP1");
    AddCodeLine("lda TMP1,#1");
    AddCodeLine("xor #0xff");
    AddCodeLine("add #0");
    AddCodeLine("sta TMP1,#1");
    AddCodeLine("lda TMP1,#2");
    AddCodeLine("xor #0xff");
    AddCodeLine("add #0");
    AddCodeLine("sta TMP1,#2");
    AddCodeLine("lda TMP1,#3");
    AddCodeLine("xor #0xff");
    AddCodeLine("add #0");
    AddCodeLine("sta TMP1,#3");
    break;
  default:
    typeerror(flags);
  }

  g_defcodelabel(L0);
}

void g_div(unsigned flags, unsigned long val)
/* Primary = TOS / Primary */
{
  AddCodeLine_("debug %s", __FUNCTION__);
  /* Do strength reduction if the value is constant and a power of two */
  int p2;
  if ((flags & CF_CONST) && (p2 = PowerOf2(val)) >= 0) {
    /* Generate a shift instead */
    g_asr(flags, p2);
  } else {
    if (flags & CF_UNSIGNED) {
      udiv(flags, val);
    } else {
      idiv(flags, val);
    }

    AddCodeLine("lda TMP1");
    AddCodeLine("sta A");
    AddCodeLine("lda TMP1,#1");
    AddCodeLine("sta A,#1");
  }
}

void g_mod(unsigned flags, unsigned long val)
/* Primary = TOS % Primary */
{
  AddCodeLine_("debug %s", __FUNCTION__);
  int p2;

  /* Check if we can do some cost reduction */
  if ((flags & CF_CONST) && (flags & CF_UNSIGNED) && val != 0xFFFFFFFF && (p2 = PowerOf2(val)) >= 0) {
    /* We can do that with an AND operation */
    g_and(flags, val - 1);
  } else {
    if (flags & CF_UNSIGNED) {
      udiv(flags, val);
    } else {
      idiv(flags, val);
    }

    AddCodeLine("lda TMP0");
    AddCodeLine("sta A");
    AddCodeLine("lda TMP0,#1");
    AddCodeLine("sta A,#1");
  }
}

static void logic(unsigned char *op, unsigned flags, unsigned long val) {
  AddCodeLine_("debug %s", __FUNCTION__);
  /* If the right hand side is const, the lhs is not on stack but still
  ** in the primary register.
  */
  if (flags & CF_CONST) {

    switch (flags & CF_TYPEMASK) {

    case CF_CHAR:
      if (flags & CF_FORCECHAR) {
        AddCodeLine("lda A");
        AddCodeLine("%s #0x%02X", op, (unsigned char)val);
        AddCodeLine("sta A");
        return;
      }
      /* FALLTHROUGH */
    case CF_INT:
      AddCodeLine("lda A");
      AddCodeLine("%s #0x%02X", op, (unsigned char)val);
      AddCodeLine("sta A");
      AddCodeLine("lda A,#1");
      AddCodeLine("%s #0x%02X", op, (unsigned char)(val >> 8));
      AddCodeLine("sta A,#1");

      return;

    case CF_LONG:
      AddCodeLine("lda A");
      AddCodeLine("%s #0x%02X", op, (unsigned char)val);
      AddCodeLine("sta A");
      AddCodeLine("lda A,#1");
      AddCodeLine("%s #0x%02X", op, (unsigned char)(val >> 8));
      AddCodeLine("sta A,#1");
      AddCodeLine("lda A,#2");
      AddCodeLine("%s #0x%02X", op, (unsigned char)(val >> 16));
      AddCodeLine("sta A,#2");
      AddCodeLine("lda A,#3");
      AddCodeLine("%s #0x%02X", op, (unsigned char)(val >> 24));
      AddCodeLine("sta A,#3");
      return;

    default:
      typeerror(flags);
    }
  }

  switch (flags & CF_TYPEMASK) {

  case CF_CHAR:
    if (flags & CF_FORCECHAR) {
      AddCodeLine("lda A");
      AddCodeLine("%s (SP)", op);
      AddCodeLine("sta A");

      g_drop(1);
      pop(flags);
      return;
    }
    /* FALLTHROUGH */

  case CF_INT:
    AddCodeLine("lda A");
    AddCodeLine("%s (SP)", op);
    AddCodeLine("sta A");
    AddCodeLine("lda A,#1");
    AddCodeLine("%s (SP),#1", op);
    AddCodeLine("sta A,#1");

    g_drop(2);
    pop(flags);
    return;

  case CF_LONG:
    AddCodeLine("lda A");
    AddCodeLine("%s (SP)", op);
    AddCodeLine("sta A");
    AddCodeLine("lda A,#1");
    AddCodeLine("%s (SP),#1", op);
    AddCodeLine("sta A,#1");
    AddCodeLine("lda A,#2");
    AddCodeLine("%s (SP),#2", op);
    AddCodeLine("sta A,#2");
    AddCodeLine("lda A,#3");
    AddCodeLine("%s (SP),#3", op);
    AddCodeLine("sta A,#3");

    g_drop(4);
    pop(flags);
    return;

  default:
    typeerror(flags);
  }
}

void g_or(unsigned flags, unsigned long val)
/* Primary = TOS | Primary */
{
  AddCodeLine_("debug %s", __FUNCTION__);
  logic("or", flags, val);
}

void g_xor(unsigned flags, unsigned long val)
/* Primary = TOS ^ Primary */
{
  AddCodeLine_("debug %s", __FUNCTION__);
  logic("xor", flags, val);
}

void g_and(unsigned flags, unsigned long val)
/* Primary = TOS & Primary */
{
  AddCodeLine_("debug %s", __FUNCTION__);
  logic("and", flags, val);
}

void g_asr(unsigned flags, unsigned long val)
/* Primary = TOS >> Primary */
{
  AddCodeLine_("debug %s", __FUNCTION__);
  unsigned size;
  /* If the right hand side is const, the lhs is not on stack but still
  ** in the primary register.
  */
  if (flags & CF_CONST) {
    switch (flags & CF_TYPEMASK) {
    case CF_CHAR:
      if (flags & CF_FORCECHAR) {
        AddCodeLine("lda #%d", (unsigned char)(val & 0x07));
        break;
      }
      // FALLTHROUGH
    case CF_INT:
      AddCodeLine("lda #%d", (unsigned char)(val & 0x0f));
      break;
    case CF_LONG:
      AddCodeLine("lda #%d", (unsigned char)(val & 0x1f));
      break;
    default:
      typeerror(flags);
    }

    AddCodeLine("sta TMP0");
  } else {
    switch (flags & CF_TYPEMASK) {
    case CF_CHAR:
      if (flags & CF_FORCECHAR) {
        size = 1;

        AddCodeLine("lda A");
        AddCodeLine("and #0x0f");
        AddCodeLine("sta TMP0");
        AddCodeLine("lda (SP)");
        AddCodeLine("sta A");
        break;
      }
      // FALLTHROUGH
    case CF_INT:
      size = 2;

      AddCodeLine("lda A");
      AddCodeLine("and #0x0f");
      AddCodeLine("sta TMP0");
      AddCodeLine("lda (SP)");
      AddCodeLine("sta A");
      AddCodeLine("lda (SP), #1");
      AddCodeLine("sta A,#1");
      break;
    case CF_LONG:
      size = 4;

      AddCodeLine("lda A");
      AddCodeLine("and #0x1f");
      AddCodeLine("sta TMP0");
      AddCodeLine("lda (SP)");
      AddCodeLine("sta A");
      AddCodeLine("lda (SP), #1");
      AddCodeLine("sta A,#1");
      AddCodeLine("lda (SP), #2");
      AddCodeLine("sta A,#2");
      AddCodeLine("lda (SP), #31");
      AddCodeLine("sta A,#3");
      break;
    default:
      typeerror(flags);
    }
  }

  unsigned L0 = GetLocalLabel();
  unsigned L1 = GetLocalLabel();

  g_defcodelabel(L0);
  AddCodeLine("lda TMP0");
  AddCodeLine("jz %s", LocalLabelName(L1));

  if (flags & CF_UNSIGNED) {
    AddCodeLine("clc");
  } else {
    AddCodeLine("clc");
    AddCodeLine("lda A,#%d", size - 1);
    AddCodeLine("and #0x80");
    AddCodeLine("sl");
  }

  switch (flags & CF_TYPEMASK) {
  case CF_CHAR:
    if (flags & CF_FORCECHAR) {
      AddCodeLine("lda A");
      AddCodeLine("sr");
      AddCodeLine("sta A");
      break;
    }
  case CF_INT:
    AddCodeLine("lda A,#1");
    AddCodeLine("sr");
    AddCodeLine("sta A,#1");
    AddCodeLine("lda A");
    AddCodeLine("sr");
    AddCodeLine("sta A");

    break;
  case CF_LONG:
    AddCodeLine("lda A,#3");
    AddCodeLine("sr");
    AddCodeLine("sta A,#3");
    AddCodeLine("lda A,#2");
    AddCodeLine("sr");
    AddCodeLine("sta A,#2");
    AddCodeLine("lda A,#1");
    AddCodeLine("sr");
    AddCodeLine("sta A,#1");
    AddCodeLine("lda A");
    AddCodeLine("sr");
    AddCodeLine("sta A");

    break;
  default:
    typeerror(flags);
    break;
  }

  AddCodeLine("sec");
  AddCodeLine("lda TMP0");
  AddCodeLine("sub #1");
  AddCodeLine("sta TMP0");
  AddCodeLine("jmp %s", LocalLabelName(L0));
  g_defcodelabel(L1);

  if (!(flags & CF_CONST)) {
    g_drop(size);
    pop(flags);
  }
}

void g_asl(unsigned flags, unsigned long val)
/* Primary = TOS << Primary */
{
  AddCodeLine_("debug %s", __FUNCTION__);
  unsigned drop;
  /* If the right hand side is const, the lhs is not on stack but still
  ** in the primary register.
  */
  if (flags & CF_CONST) {
    switch (flags & CF_TYPEMASK) {
    case CF_CHAR:
      if (flags & CF_FORCECHAR) {
        AddCodeLine("lda #%d", (unsigned char)(val & 0x07));
        break;
      }
      // FALLTHROUGH
    case CF_INT:
      AddCodeLine("lda #%d", (unsigned char)(val & 0x0f));
      break;
    case CF_LONG:
      AddCodeLine("lda #%d", (unsigned char)(val & 0x1f));
      break;
    default:
      typeerror(flags);
    }

    AddCodeLine("sta TMP0");
  } else {
    switch (flags & CF_TYPEMASK) {
    case CF_CHAR:
      if (flags & CF_FORCECHAR) {
        drop = 1;

        AddCodeLine("lda A");
        AddCodeLine("and #0x0f");
        AddCodeLine("sta TMP0");
        AddCodeLine("lda (SP)");
        AddCodeLine("sta A");
        break;
      }
      // FALLTHROUGH
    case CF_INT:
      drop = 2;

      AddCodeLine("lda A");
      AddCodeLine("and #0x0f");
      AddCodeLine("sta TMP0");
      AddCodeLine("lda (SP)");
      AddCodeLine("sta A");
      AddCodeLine("lda (SP), #1");
      AddCodeLine("sta A,#1");
      break;
    case CF_LONG:
      drop = 4;

      AddCodeLine("lda A");
      AddCodeLine("and #0x1f");
      AddCodeLine("sta TMP0");
      AddCodeLine("lda (SP)");
      AddCodeLine("sta A");
      AddCodeLine("lda (SP), #1");
      AddCodeLine("sta A,#1");
      AddCodeLine("lda (SP), #2");
      AddCodeLine("sta A,#2");
      AddCodeLine("lda (SP), #31");
      AddCodeLine("sta A,#3");
      break;
    default:
      typeerror(flags);
    }
  }

  unsigned L0 = GetLocalLabel();
  unsigned L1 = GetLocalLabel();

  g_defcodelabel(L0);
  AddCodeLine("lda TMP0");
  AddCodeLine("jz %s", LocalLabelName(L1));
  AddCodeLine("clc");

  switch (flags & CF_TYPEMASK) {
  case CF_CHAR:
    if (flags & CF_FORCECHAR) {
      AddCodeLine("lda A");
      AddCodeLine("sl");
      AddCodeLine("sta A");
      break;
    }
    // FALLTHROUGH
  case CF_INT:
    AddCodeLine("lda A");
    AddCodeLine("sl");
    AddCodeLine("sta A");
    AddCodeLine("lda A,#1");
    AddCodeLine("sl");
    AddCodeLine("sta A,#1");
    break;
  case CF_LONG:
    AddCodeLine("lda A");
    AddCodeLine("sl");
    AddCodeLine("sta A");
    AddCodeLine("lda A,#1");
    AddCodeLine("sl");
    AddCodeLine("sta A,#1");
    AddCodeLine("lda A,#2");
    AddCodeLine("sl");
    AddCodeLine("sta A,#2");
    AddCodeLine("lda A,#3");
    AddCodeLine("sl");
    AddCodeLine("sta A,#3");
    break;
  default:
    typeerror(flags);
    break;
  }

  AddCodeLine("sec");
  AddCodeLine("lda TMP0");
  AddCodeLine("sub #1");
  AddCodeLine("sta TMP0");
  AddCodeLine("jmp %s", LocalLabelName(L0));
  g_defcodelabel(L1);

  if (!(flags & CF_CONST)) {
    g_drop(drop);
    pop(flags);
  }
}

void g_neg(unsigned Flags)
/* Primary = -Primary */
{
  AddCodeLine_("debug %s", __FUNCTION__);
  switch (Flags & CF_TYPEMASK) {

  case CF_CHAR:
    if (Flags & CF_FORCECHAR) {
      AddCodeLine("clc");
      AddCodeLine("lda A");
      AddCodeLine("xor #0xff");
      AddCodeLine("add #1");
      AddCodeLine("sta A");
      return;
    }
    /* FALLTHROUGH */

  case CF_INT:
    AddCodeLine("clc");
    AddCodeLine("lda A");
    AddCodeLine("xor #0xff");
    AddCodeLine("add #1");
    AddCodeLine("sta A");
    AddCodeLine("lda A,#1");
    AddCodeLine("xor #0xff");
    AddCodeLine("add #0");
    AddCodeLine("sta A,#1");
    break;

  case CF_LONG:
    AddCodeLine("clc");
    AddCodeLine("lda A");
    AddCodeLine("xor #0xff");
    AddCodeLine("add #1");
    AddCodeLine("sta A");
    AddCodeLine("lda A,#1");
    AddCodeLine("xor #0xff");
    AddCodeLine("add #0");
    AddCodeLine("sta A,#1");
    AddCodeLine("lda A,#2");
    AddCodeLine("xor #0xff");
    AddCodeLine("add #0");
    AddCodeLine("sta A,#2");
    AddCodeLine("lda A,#3");
    AddCodeLine("xor #0xff");
    AddCodeLine("add #0");
    AddCodeLine("sta A,#3");

    break;

  default:
    typeerror(Flags);
  }
}

void g_bneg(unsigned flags)
/* Primary = !Primary */
{
  AddCodeLine_("debug %s", __FUNCTION__);
  unsigned L0 = GetLocalLabel();
  unsigned L1 = GetLocalLabel();

  switch (flags & CF_TYPEMASK) {

  case CF_CHAR:
    AddCodeLine("lda A");
    AddCodeLine("jnz %s", LocalLabelName(L0));
    AddCodeLine("lda #1");
    AddCodeLine("jmp %s", LocalLabelName(L1));
    g_defcodelabel(L0);
    AddCodeLine("lda #0");
    g_defcodelabel(L1);
    AddCodeLine("sta A");

    break;

  case CF_INT:
    AddCodeLine("lda A");
    AddCodeLine("or A,#1");
    AddCodeLine("jnz %s", LocalLabelName(L0));
    AddCodeLine("lda #1");
    AddCodeLine("jmp %s", LocalLabelName(L1));
    g_defcodelabel(L0);
    AddCodeLine("lda #0");
    g_defcodelabel(L1);
    AddCodeLine("sta A");
    AddCodeLine("lda #0");
    AddCodeLine("sta A,#1");
    break;

  case CF_LONG:
    AddCodeLine("lda A");
    AddCodeLine("or A,#1");
    AddCodeLine("or A,#2");
    AddCodeLine("or A,#3");
    AddCodeLine("jnz %s", LocalLabelName(L0));
    AddCodeLine("lda #1");
    AddCodeLine("jmp %s", LocalLabelName(L1));
    g_defcodelabel(L0);
    AddCodeLine("lda #0");
    g_defcodelabel(L1);
    AddCodeLine("sta A");
    AddCodeLine("lda #0");
    AddCodeLine("sta A,#1");
    AddCodeLine("sta A,#2");
    AddCodeLine("sta A,#3");
    break;

  default:
    typeerror(flags);
  }
}

void g_com(unsigned Flags)
/* Primary = ~Primary */
{
  AddCodeLine_("debug %s", __FUNCTION__);
  switch (Flags & CF_TYPEMASK) {

  case CF_CHAR:
    if (Flags & CF_FORCECHAR) {
      AddCodeLine("lda A");
      AddCodeLine("xor #0xff");
      AddCodeLine("sta A");
      return;
    }
    /* FALLTHROUGH */

  case CF_INT:
    AddCodeLine("lda A");
    AddCodeLine("xor #0xff");
    AddCodeLine("sta A");
    AddCodeLine("lda A,#1");
    AddCodeLine("xor #0xff");
    AddCodeLine("sta A,#1");
    break;

  case CF_LONG:
    AddCodeLine("lda A");
    AddCodeLine("xor #0xff");
    AddCodeLine("sta A");
    AddCodeLine("lda A,#1");
    AddCodeLine("xor #0xff");
    AddCodeLine("sta A,#1");
    AddCodeLine("lda A,#2");
    AddCodeLine("xor #0xff");
    AddCodeLine("sta A,#2");
    AddCodeLine("lda A,#3");
    AddCodeLine("xor #0xff");
    AddCodeLine("sta A,#3");
    break;

  default:
    typeerror(Flags);
  }
}

void g_inc(unsigned flags, unsigned long val)
/* Increment the primary register by a given number */
{
  AddCodeLine_("debug %s", __FUNCTION__);
  /* Don't inc by zero */
  if (val == 0) {
    return;
  }

  g_add(flags | CF_CONST, val);
}

void g_dec(unsigned flags, unsigned long val)
/* Decrement the primary register by a given number */
{
  AddCodeLine_("debug %s", __FUNCTION__);
  /* Don't dec by zero */
  if (val == 0) {
    return;
  }

  g_sub(flags | CF_CONST, val);
}

/*
** Following are the conditional operators. They compare the TOS against
** the primary and put a literal 1 in the primary if the condition is
** true, otherwise they clear the primary register
*/

void g_eq(unsigned flags, unsigned long val)
/* Test for equal */
{
  AddCodeLine_("debug %s", __FUNCTION__);
  // TMP1 == 1 && TMP0 == 0
  unsigned L = GetLocalLabel();
  cmp(flags, val);
  AddCodeLine("lda #0");
  AddCodeLine("sta A");
  AddCodeLine("sta A,#1");

  AddCodeLine("lda TMP0");
  AddCodeLine("jnz %s", LocalLabelName(L));
  AddCodeLine("lda TMP1");
  AddCodeLine("sta A");
  g_defcodelabel(L);
}

void g_ne(unsigned flags, unsigned long val)
/* Test for not equal */
{
  AddCodeLine_("debug %s", __FUNCTION__);
  // TMP1 == 0 || TMP0 != 0
  g_eq(flags, val);
  AddCodeLine("lda A");
  AddCodeLine("xor #1");
  AddCodeLine("sta A");
}

void g_lt(unsigned flags, unsigned long val)
/* Test for less than */
{
  AddCodeLine_("debug %s", __FUNCTION__);
  // TMP1 == 0
  cmp(flags, val);
  AddCodeLine("lda TMP1");
  AddCodeLine("xor #1");
  AddCodeLine("sta A");
  AddCodeLine("lda #0");
  AddCodeLine("sta A,#1");
}

void g_le(unsigned flags, unsigned long val)
/* Test for less than or equal to */
{
  AddCodeLine_("debug %s", __FUNCTION__);
  // (TMP1 == 0) || (TMP1 == 1 && TMP0 == 0)
  g_gt(flags, val);
  AddCodeLine("lda A");
  AddCodeLine("xor #1");
  AddCodeLine("sta A");
}

void g_gt(unsigned flags, unsigned long val)
/* Test for greater than */
{
  AddCodeLine_("debug %s", __FUNCTION__);
  // TMP1 == 1 && TMP0 != 0
  unsigned L = GetLocalLabel();
  cmp(flags, val);
  AddCodeLine("lda #0");
  AddCodeLine("sta A");
  AddCodeLine("sta A,#1");

  AddCodeLine("lda TMP0");
  AddCodeLine("jz %s", LocalLabelName(L));
  AddCodeLine("lda TMP1");
  AddCodeLine("sta A");
  g_defcodelabel(L);
}

void g_ge(unsigned flags, unsigned long val)
/* Test for greater than or equal to */
{
  AddCodeLine_("debug %s", __FUNCTION__);
  // TMP1 == 1
  cmp(flags, val);
  AddCodeLine("lda TMP1");
  AddCodeLine("sta A");
  AddCodeLine("lda #0");
  AddCodeLine("sta A,#1");
}

void g_test(unsigned flags)
/* Test the value in the primary and set the condition codes */
{
  AddCodeLine_("debug %s", __FUNCTION__);
  switch (flags & CF_TYPEMASK) {

  case CF_CHAR:
    if (flags & CF_FORCECHAR) {
      AddCodeLine("lda A");
      AddCodeLine("sta A,#1");
      break;
    }
    /* FALLTHROUGH */

  case CF_INT:
    AddCodeLine("lda A");
    AddCodeLine("or A,#1");
    AddCodeLine("sta A");
    break;

  case CF_LONG:
    printf("> TODO g_test long\n");
    if (flags & CF_UNSIGNED) {
      AddCodeLine("jsr utsteax");
    } else {
      AddCodeLine("jsr tsteax");
    }
    break;

  default:
    typeerror(flags);
  }
}

void g_cmp(unsigned flags, unsigned long val)
/* Immidiate compare. The primary register will not be changed, Z flag
** will be set.
*/
{
  AddCodeLine_("debug %s", __FUNCTION__);
  printf("> TODO %s\n", __FUNCTION__);
  unsigned L;

  /* Check the size and determine operation */
  switch (flags & CF_TYPEMASK) {

  case CF_CHAR:
    if (flags & CF_FORCECHAR) {
      AddCodeLine("cmp #$%02X", (unsigned char)val);
      break;
    }
    /* FALLTHROUGH */

  case CF_INT:
    L = GetLocalLabel();
    AddCodeLine("cmp #$%02X", (unsigned char)val);
    AddCodeLine("bne %s", LocalLabelName(L));
    AddCodeLine("cpx #$%02X", (unsigned char)(val >> 8));
    g_defcodelabel(L);
    break;

  case CF_LONG:
    Internal("g_cmp: Long compares not implemented");
    break;

  default:
    typeerror(flags);
  }
}
